package com.moyun.common.config.apiDocument;

import com.fasterxml.classmate.TypeResolver;
import com.moyun.syssecurity.sms.pojo.SMSVO;
import org.apache.commons.compress.utils.Sets;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.OperationBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiDescription;
import springfox.documentation.service.Operation;
import springfox.documentation.service.Parameter;
import springfox.documentation.service.Tag;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ApiListingScannerPlugin;
import springfox.documentation.spi.service.contexts.DocumentationContext;
import springfox.documentation.spring.web.readers.operation.CachingOperationNameGenerator;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @ClassName SpringSecurityApis
 * @Description 由于 Spring Security 的登录、登出接口是通过Filter实现，导致 Swagger 无法获取其信息。 这里手动将登录、登出接口注册到Swagger中，在Swagger-UI才能展示，方便调用。
 * 文档学习地址 : https://blog.csdn.net/qq_31772441/article/details/119768811
 * 官网中文翻译 : https://uhfun.gitee.io/uhfun-jekyll/tech/2020/05/13/Springfox%E5%8F%82%E8%80%83%E6%96%87%E6%A1%A3-Springfox-Reference-Documentation%E4%B8%AD%E6%96%87%E7%BF%BB%E8%AF%91.html#1-%E4%BB%8B%E7%BB%8D
 * @Author yangboxuan
 * @Date 2022/6/23 22:51
 */
@Component
public class SpringSecurityApis implements ApiListingScannerPlugin {

    @Override
    public List<ApiDescription> apply(DocumentationContext documentationContext) {

        //登录接口
        //1.定义参数
        Parameter username = new ParameterBuilder()
                //参数名称
                .name("username")
                //参数描述
                .description("用户名")
                //参数数据类型
                .type(new TypeResolver().resolve(String.class))
                //参数 数据类型 即 JAVA 类型
                .modelRef(new ModelRef("string"))
                //参数类型 schema
                .parameterType("form")
                //是否必填
                .required(true)
                //参数默认值
                .defaultValue("admin")
                //参数顺序
                .order(1)
                .build();
        Parameter password = new ParameterBuilder()
                .name("password")
                .description("密码")
                .type(new TypeResolver().resolve(String.class))
                .modelRef(new ModelRef("string"))
                .parameterType("form")
                .required(true)
                .defaultValue("123456")
                .order(2)
                .build();
        //2.接口的每种请求方式(GET/POST...)为一个 Operation
        Operation loginOperation = new OperationBuilder(new CachingOperationNameGenerator())
                //uniqueId  必须添加,否则相同的类型的 Operation 对象就会互相覆盖
                .uniqueId("loginOperation")
                //http请求类型
                .method(HttpMethod.POST)
                .summary("登录")
                //归类标签
                .tags(Sets.newHashSet("Authentication(系统认证)"))
                .responseMessages(Sets.newHashSet(new ResponseMessageBuilder().code(200).message("OK").build()))
                .consumes(Sets.newHashSet(MediaType.MULTIPART_FORM_DATA_VALUE))
                .produces(Sets.newHashSet(MediaType.APPLICATION_JSON_VALUE))
                .parameters(Arrays.asList(username, password))
                .build();
        //3.每个接口路径对应一个 ApiDescription
        ApiDescription loginDesc = new ApiDescription(null, "/login", "登录", Collections.singletonList(loginOperation), false);

        //登出接口
        Operation logoutOperation = new OperationBuilder(new CachingOperationNameGenerator())
                .uniqueId("logoutOperation")
                .method(HttpMethod.GET)
                .summary("登出")
                .notes("退出登录")
                .tags(Sets.newHashSet("Authentication(系统认证)"))
                .responseMessages(Sets.newHashSet(new ResponseMessageBuilder().code(200).message("OK").build()))
                .build();
        ApiDescription logoutDesc = new ApiDescription(null, "/logout", "注销", Collections.singletonList(logoutOperation), false);

        //登录接口
        //1.定义参数
        Parameter smsLoginParams = new ParameterBuilder()
                //参数名称
                .name("smsLoginParams")
                //参数说明
                .description("短信登录参数")
                //数据类型
                .type(new TypeResolver().resolve(SMSVO.class))
                //schema
                .modelRef(new ModelRef("SMSVO"))
                //请求类型
                .parameterType("body")
                //是否必填
                .required(true)
                //参数顺序
                .order(1)
                .build();
        //2.接口的每种请求方式(GET/POST...)为一个 Operation
        Operation smsLoginOperation = new OperationBuilder(new CachingOperationNameGenerator())
                .uniqueId("smsLoginOperation")
                //http请求类型
                .method(HttpMethod.POST)
                .summary("短信登录")
                //归类标签
                .tags(Sets.newHashSet("Authentication(系统认证)"))
                .responseMessages(Sets.newHashSet(new ResponseMessageBuilder().code(200).message("OK").build()))
                .consumes(Sets.newHashSet(MediaType.APPLICATION_JSON_VALUE))
                .produces(Sets.newHashSet(MediaType.APPLICATION_JSON_VALUE))
                .parameters(Collections.singletonList(smsLoginParams))
                .build();
        //3.每个接口路径对应一个 ApiDescription
        ApiDescription SMSLoginDesc = new ApiDescription(null, "/sms/login", "短信登录", Collections.singletonList(smsLoginOperation), false);


        documentationContext.getTags().add(new Tag("Authentication(系统认证)", "登录、登出"));

        return new ArrayList<>(Arrays.asList(loginDesc, logoutDesc, SMSLoginDesc));
    }

    @Override
    public boolean supports(DocumentationType documentationType) {
        return DocumentationType.SWAGGER_2.equals(documentationType);
    }

}
